using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UIObjects.TreeViewInterfaces;
#if SILVERLIGHT
using AbstraX.ClientInterfaces;
#else
using AbstraX.ServerInterfaces;
#endif
using System.Collections.ObjectModel;
using AbstraX.Contracts;
using MvvmTreeView;
using System.Windows;
using SL4PopupMenu;
using System.Diagnostics;
using System.ComponentModel;

namespace AbstraX.ViewModels.ModelTree
{
    public class ModelTreeNodeElement : ModelTreeNodeBase
    {
        private IElement element;
        private bool allowRename;
        private string name;

        public ModelTreeNodeElement(IProviderClientService providerService, ITreeView treeView, IElement element) : base(providerService, treeView)
        {
            this.element = element;
            this.Tag = element;
            this.name = element.Name;

            var baseType = element.DataType;

#if SILVERLIGHT
            this.abstraXExtension = element.LoadExtension(element, this);
            
            if (abstraXExtension != null)
            {
                allowRename = this.abstraXExtension.DoesAllowRename(element, this);
            }
#endif
        }

        public override bool AllowRename
        {
            get
            {
                return allowRename;
            }

        }

        public override ContextMenu ContextMenu
        {
            get
            {
                ContextMenu contextMenu = null;
                ContextMenuItem buildMenuItem;

                RoutedEventHandler clickHandler = (sender, r) =>
                {
                    var menuItem = (PopupMenuItem)sender;
                    var invokeOperation = providerService.Build((string)menuItem.Tag);

                    invokeOperation.OnBuildCompleted += (sender2, e) =>
                    {
                        if (invokeOperation.HasException)
                        {
                            Debugger.Break();
                        }
                    };

                    invokeOperation.ExceptionOccured += (sender2, e) =>
                    {
                        MessageBox.Show(e.Exception.Message);
                    };
                };

                buildMenuItem = new ContextMenuItem("BuildMiddleLayer", null, "Build Middle Layer...", element.ID, ContextMenuItemType.View, clickHandler);

                if (abstraXExtension != null)
                {
                    contextMenu = abstraXExtension.ContextMenu;
                }

                if (contextMenu == null)
                {
                    contextMenu = new ContextMenu
                    {
                        Items = new List<IContextMenuItem>()
                        {
                            buildMenuItem
                        }
                    };
                }
                else if (!contextMenu.Items.Any(m => m.ID == buildMenuItem.ID))
                {
                    contextMenu.Items.Add(buildMenuItem);
                }

                return contextMenu;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override IBase BaseObject
        {
            get
            {
                return element;
            }
        }

        public override string Text
        {
            get
            {
                return this.name;
            }

            set
            {
                this.name = value;
                OnPropertyChanged(new System.ComponentModel.PropertyChangedEventArgs("Text"));
            }
        }

        public override void LoadChildren()
        {
            var element = (IElement)this.Tag;

#if SILVERLIGHT
            element.ChildrenLoaded += new ChildrenLoadedHandler(ChildrenLoaded);
            element.LoadChildren();
#endif
            treeView.SetBusy(this, true);
        }

        protected override void ChildrenLoaded(IBase parent)
        {
            isChildrenLoaded = true;

            var nodeIds = this.Nodes.Where(n => (!(n is ModelTreeNodeFolder)) && (!(n is SpinsterNode)) && (!(n is EditNode))).Select(n => (IBase)n.Tag).Select(b => b.ID);

            // elements

            if (element.ChildElements != null)
            {
                var elements = element.ChildElements.AsQueryable().Cast<IElement>();
                var folderElements = elements.Where(e => e.FolderKeyPair != null && e.FolderKeyPair.Length > 0).Cast<IBase>();
                var nonFolderElements = elements.Where(e => e.FolderKeyPair == null || e.FolderKeyPair.Length == 0);

                if (folderElements.Count() > 0)
                {
                    var folderKeyPairs = folderElements.Select(e => e.FolderKeyPair).Distinct();

                    // add new

                    folderKeyPairs
                    .Where(fkp => !this.Nodes.Where(n => n is ModelTreeNodeFolder).Cast<ModelTreeNodeFolder>().Any(f => f.FolderKeyPair == fkp))
                    .Select(fkp => new ModelTreeNodeFolder(providerService, treeView, fkp, folderElements.Where(f => f.FolderKeyPair == fkp)))
                    .OrderBy(e => e.ChildOrdinal)
                    .ThenBy(e => e.Text)
                    .ToList()
                    .ForEach(f => this.Nodes.InsertOrderBy(f2 => f2.ChildOrdinal).ThenBy(f2 => f2.Text, f));  // always remember to include the second parameter to finalize
                }

                nonFolderElements.AsQueryable()
                .Where(e => e.FolderKeyPair == null)
                .Select(r => new ModelTreeNodeElement(providerService, treeView, r)).Where(d => !nodeIds.Any(id => id == ((IBase)(d.Tag)).ID))
                .ToList()
                .ForEach(d => this.Nodes.InsertOrderBy(t => t.ChildOrdinal).ThenBy(t => t.Text, d));
            }

            // attributes

            if (element.Attributes != null)
            {
                var attributes = element.Attributes.AsQueryable().Cast<IAttribute>();
                var folderAttributes = attributes.Where(e => e.FolderKeyPair != null && e.FolderKeyPair.Length > 0).Cast<IBase>();
                var nonFolderAttributes = attributes.Where(e => e.FolderKeyPair == null || e.FolderKeyPair.Length == 0);

                if (folderAttributes.Count() > 0)
                {
                    var folderKeyPairs = folderAttributes.Select(e => e.FolderKeyPair).Distinct();

                    folderKeyPairs
                    .Select(fkp => new ModelTreeNodeFolder(providerService, treeView, fkp, folderAttributes.Where(f => f.FolderKeyPair == fkp)))
                    .ToList()
                    .ForEach(f => this.Nodes.InsertOrderBy(f2 => f2.ChildOrdinal).ThenBy(f2 => f2.Text, f));
                }

                nonFolderAttributes.AsQueryable()
                .Where(e => e.FolderKeyPair == null)
                .Select(r => new ModelTreeNodeAttribute(providerService, treeView, r)).Where(d => !nodeIds.Any(id => id == ((IBase)(d.Tag)).ID))
                .OrderBy(e => e.Text)
                .ToList()
                .ForEach(d => this.Nodes.InsertOrderBy(t => t.ChildOrdinal).ThenBy(t => t.Text, d));
            }

            // operations

            if (element.Operations != null)
            {
                var operations = element.Operations.AsQueryable().Cast<IOperation>();
                var folderOperations = operations.Where(e => e.FolderKeyPair != null && e.FolderKeyPair.Length > 0).Cast<IBase>();
                var nonFolderOperations = operations.Where(e => e.FolderKeyPair == null || e.FolderKeyPair.Length == 0);

                if (folderOperations.Count() > 0)
                {
                    var folderKeyPairs = folderOperations.Select(e => e.FolderKeyPair).Distinct();

                    folderKeyPairs
                    .Select(fkp => new ModelTreeNodeFolder(providerService, treeView, fkp, folderOperations.Where(f => f.FolderKeyPair == fkp)))
                    .OrderBy(e => e.Text)
                    .ToList()
                    .ForEach(f => this.Nodes.InsertOrderBy(f2 => f2.ChildOrdinal).ThenBy(f2 => f2.Text, f));
                }

                nonFolderOperations.AsQueryable()
                .Where(e => e.FolderKeyPair == null)
                .Select(r => new ModelTreeNodeOperation(providerService, treeView, r)).Where(d => !nodeIds.Any(id => id == ((IBase)(d.Tag)).ID))
                .OrderBy(e => e.Text)
                .ToList()
                .ForEach(d => this.Nodes.InsertOrderBy(t => t.ChildOrdinal).ThenBy(t => t.Text, d));
            }

            this.RemoveSpinster();
            this.RemoveEditNode();

            treeView.SetBusy(this, false);
        }

        public override List<string> AddNewShortCuts
        {
            get { return new List<string>(); }
        }

        public override void OnSelect()
        {
            var type = element.DataType;
            this.TreeView.FireNodeSelected(this);
        }

        public override void AddNew(string shortCut)
        {
            throw new NotImplementedException();
        }

        public override void DragStarting(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e)
        {
        }

        public override void DragCompleted(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e)
        {
        }

        public override void GiveFeedback(object sender, UIObjects.DragDropInterfaces.GiveFeedbackEventArgs e)
        {
        }

        public override void ItemDropped(object sender, UIObjects.DragDropInterfaces.ItemDragEventArgs e, int insertionIndex)
        {
        }

        public override void DragEnter(object sender, UIObjects.DragDropInterfaces.DragEventArgs e)
        {
        }

        public override void DragLeave(object sender, UIObjects.DragDropInterfaces.DragEventArgs e)
        {
        }

        public override void DragOver(object sender, UIObjects.DragDropInterfaces.DragEventArgs e)
        {
        }

        public override float ChildOrdinal
        {
            get
            {
                return element.ChildOrdinal;
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override bool? HasChildren
        {
            get
            {
                return element.HasChildren;
            }
        }
    }
}
